# 1.1 模块的基本概念 ## 基于模块的系统设计 **模块**(Verilog Module)是Verilog HDL语言的基本单元,被用于描述某个设计的**功能、结构**及其与其他模块通信的**外部接口**。 ![image1](../../../../resources/3064983f50044dfabc8321169285f2d0.png) 基于模块的层次化系统设计思想:将复杂系统按照不同功能定义划分成多个小功能模块,对各模块进行独立设计开发后再组装到一起。系统设计一般按下面步骤进行: 1. 把系统划分成模块(及其子模块); 2. 规划各个模块的接口; 3. 对模块编程并连接各模块,完成系统设计。 ## 模块的基本结构 1. 模块端口定义 端口是模块和外界进行信息交互的接口。有些模块(特别是测试代码)与外界无信息交互,则不需要端口列表。 ```verilog // 格式 module module_name(port1, port2, port3, ...); // 举例 module add(a, b, q); ``` 2. 输入输出说明 标识端口方向,关键字:`input`(输入),`output`(输出),`inout`(双向) ```verilog input port1_name, port2_name, ...; //位宽为1的输入端 output [w-1:0] port_name; //位宽为w的输出端 inout [t:1] port_name; //位宽为t的双向总线端 ``` 也可以在端口定义语句中直接声明输入输出: ```verilog module module_name(input port1, output port2, inout port3, ...); ``` 3. 参数及信号类型说明 `wire`(线网型信号)描述的硬件是连线,实时变化,没有存储记忆能力 `reg`(寄存器型信号)描述的硬件可能是寄存器,寄存器中的值在两次赋值之间保持不变 `parameter`(参数)声明一个可变常量,常用于定义延时及宽度变量 ```verilog wire variable1_name, variable2_name; //表示位宽为1的两个线网型信号 reg [w-1:0] variable_name; //表示位宽为w的寄存器型信号 wire [t:1] var1_name, var2_name; //表示位宽为t的两个线网型信号 parameter p1_name = exp1, p2_name = exp2; //表示两个参数常量 // 举例 parameter size = 8; input [size-1] a, b; ``` 4. 逻辑功能说明 `assign`(连续赋值),用于定义逻辑功能 `always`(过程赋值),检测敏感信号并描述逻辑功能 ```verilog assign a = b & c; // 该语句描述了一个二输入与门,a为输出端,b、c为输入端 always @ (posedge clk) // 检测到clk信号上升沿时,执行下面的语句 begin ; end ``` 5. 模块结尾标识:`endmodule` ## 模块模板 ```verilog // 定义模块 module (); output ; // 输出端口声明 input ; // 输入端口声明 // 定义数据,信号的类型,函数声明 wire ; reg ; //逻辑功能定义 assign =; // 定义逻辑功能 always @ () // 块描述逻辑功能 begin // 过程赋值 // if-else,case 语句 // while,repeat,for 循环语句 // task,function 调用 end // 调用其他模块 (); // 门元件例化 (); endmodule ``` 1. 例:三人表决问题 ```verilog module vote(a, b, c, f); // 模块名与端口列表 input a,b,c; // 模块的输入端口 output f; // 模块的输出端口 wire a,b,c,f; // 定义信号的数据类型 assign f=(a&b)|(a&c)|(b&c); // 逻辑功能描述 endmodule ``` 2. 例:4位BCD码加法计算 ```verilog module add4_bcd(cout, sum, ina, inb, cin); input cin; input [3:0] ina, inb; output [3:0] sum; reg [3:0] sum; output cout; reg cout; reg [4:0] temp; always @ (ina, inb, cin) begin temp<=ina+inb+cin; if(temp>9) {cout, sum}<=temp+6; else {cout, sum}<=temp; end endmodule ``` ## 模块的调用 Verilog HDL 通过“模块调用”(或称为“模块实例化”)实现子模块与高层模块的连接。 1. **位置关联模式**:在引用时,各连接端口严格按照模块定义时的端口顺序来依次连接,不用标明原模块定义时规定的端口名。 ```verilog // 格式 module_name use_name(sig1_name, sig2_name,...); // 如果已经定义模块 module MyDesign(sin, pout); // 调用该模块时链接外部信号SerialIn和ParallelOut MyDesign U1(SerialIn, ParallelOut); ``` 2. **名称关联模式**:端口列表内指明与每个连接端口信号相连的模块端口名。 ```verilog // 格式 module_name use_name(.port1_name(sig1_name),...); // 前述例子可以改写为 MyDesign U1(.sin(SerialIn), .pout(ParallelOut)); ``` 3. **调用内置模块**:对于普通门,端口列表的顺序是:(output, input1, input2,...) ```verilog // 格式 module_name use_name(sig1_name, sig2_name,...) // 调用三输入与门 and U1(out, in1, in2, in3); ``` ## 测试代码 例:“与-或-非”电路模块 $ f= \overline{a \cdot b + \overline{c \cdot d}}$ 1. 与-或-非电路模块示例 ```verilog module aoi(a,b,c,d,f); // 模块名为aoi,端口列表a,b,c,d,f input a,b,c,d; // 模块的输入端口为 a b c d output f; // 模块的输出端口为 f wire a,b,c,d,f; //定义信号的数据类型 assign f=~((a&b)|(~(c&d))); //逻辑功能描述 endmodule ``` 模块结构要点: - 每个模块内容都嵌在 `module` 和 `endmodule` 之间 - 每个模块由两部分组成: - **描述接口**:模块首先要进行端口定义,说明输入和输出(`input`,`output`,`inout`) - **描述逻辑功能** - 除了 `endmodule` 等少数语句外,每个语句的最后必须有英文分号标记其结束 - 一行可写多个语句,一个语句也可写成多行 - 行注释 `//`,块注释 `/*......*/` 2. 测试代码 **测试代码**(testbench 或 testfixture):通过测试代码调用需要验证的模块,用一段程序描述信号的变化,产生输入信号波形(激励信号)输入被测试电路的 HDL 模型,记录模型输出,实现仿真分析。 例:针对上述"与-或-非"电路模块的测试代码: ```verilog `timescale 1ns/1ps module aoi_tb; // 定义测试模块 reg a,b,c,d; // 变量 a,b,c,d 用于产生变化输入 wire f; // 变量 f 用于记录输出 initial // Verilog关键字用于初始化 begin a=0; b=0; c=0; d=0; // 仿真 0 时刻输入信号的值 #100; a=1; b=0; c=0; d=1; // 仿真 100ns 时输入信号值 #100; a=1; b=1; c=1; d=0; // 仿真 200ns 时输入信号值 #100; a=0; b=0; c=1; d=1; // 仿真 300ns 时输入信号值 #100; a=0; b=1; c=1; d=1; // 仿真 400ns 时输入信号值 #100; a=1; b=1; c=1; d=1; // 仿真 500ns 时输入信号值 end aoi u1(.a(a),.b(b),.c(c),.d(d),.f(f)); // 调用被仿真电路的模型 endmodule ``` 测试代码特点: - 测试代码也使用 `module` 定义,但没有输入输出; - 测试代码带有时间控制功能(`#100`); - 测试代码调用被测试模块。